package com.woniu.controller;


import com.woniu.model.Result;
import com.woniu.service.FootprintService;
import com.woniu.service.TestDetailService;
import com.woniu.service.UserService;
import com.woniu.service.util.FileUpload;
import com.woniu.service.util.Md5Util;
import com.woniu.service.util.UserTokenUtil;
import com.woniu.service.vo.TestPageVo;
import com.woniu.service.vo.TestVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author D
 * @since 2023-03-27 08:24:57
 */
@RestController
@RequestMapping("/testDetail")
@Slf4j
public class TestDetailController {
    @Autowired
    private TestDetailService testDetailService;
    @Autowired
    private FootprintService footprintService;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private UserService userService;

    /**
     * 获取指定靶场的详细信息并在Redis缓存中查找靶场所映射的端口号，将其拼接为靶场URL
     * @param testId 靶场环境的ID
     * @param request HTTP请求对象
     * @return 返回包含靶场详细信息的Result对象
     */
    @GetMapping("/getTestDetail")
    public Result<?> getTestDetail(Long testId, HttpServletRequest request) {

        Long userCode = Long.valueOf(UserTokenUtil.getUserAccount(request));
        Integer result = footprintService.saveHistory(testId, userCode);
        TestVo testDetail = testDetailService.getTestDetail(testId);

        if (testDetail == null || result != 1) {
            return Result.failResult("没有该测试");
        }
        testDetail.setTestUrl("等待测试中");
        String port = (String) redisTemplate.opsForValue().get("-"+userCode + "port" + testId);
        if (port != null){
            testDetail.setTestUrl(port);
        }
        return Result.successResult(testDetail);
    }
    /**
     * 获取指定靶场的靶场详情
     *
     * @param testCode 靶场code
     * @return 返回靶场详情
     */
    @RequestMapping("getOne")
    public Result<TestVo> getOne(Long testCode) {
        TestVo testVo = testDetailService.getOneTestDetail(testCode);
        return Result.successResult(testVo);
    }
    /**
     * 获取所有靶场详情
     *
     * @param request Request对象
     * @param input 模糊查询输入内容
     * @param currentPage 当前页数
     * @param size 页面大小
     * @return 返回靶场分页信息
     */
    @RequestMapping("getAllTestDetail")
    public Result<TestPageVo> getAllTestDetail(HttpServletRequest request, String input, Long currentPage, Long size) {
        Result result = new Result<>();
         Long userCode = Long.valueOf(UserTokenUtil.getUserAccount(request));
        TestPageVo testPageVo = testDetailService.getAllTestDetail(userCode, input, currentPage, size);
        if (testPageVo==null||testPageVo.getPageVo().size()==0){
            result=Result.failResult("未找到靶场");
        }else {
            result=Result.successResult(testPageVo);
        }
        return result;
    }
    /**
     * 添加靶场详情
     *
     * @param request Request对象
     * @param testVo 靶场详情对象
     * @return 返回添加结果
     */
    @PostMapping("addTestDetail")
    public Result addTestDetail(HttpServletRequest request, TestVo testVo) {
        Long userCode = Long.valueOf(UserTokenUtil.getUserAccount(request));

        Result result = new Result<>();

        Boolean addResult = testDetailService.addTestDetail(userCode, testVo);
        if (addResult) {
            result = Result.successResult("添加成功！");
        } else {
            result = Result.failResult("添加失败！");
        }
        return result;
    }
    /**
     * 上传文件
     *
     * @param file 待上传的文件
     * @return 返回文件名
     */
    @RequestMapping("upload")
    public String upload(@RequestBody MultipartFile file) throws IOException {

       return FileUpload.upload(file);
    }

    /**
     * 获取靶场剩余时间
     *
     * @param request HTTP请求对象
     * @param testId  靶场ID
     * @return Result对象，包含剩余时间，单位为秒
     */
    @GetMapping("getTime")
    public Result<Object> getTime(HttpServletRequest request, Long testId) {
        String userCode = UserTokenUtil.getUserAccount(request);
        final int oneHourMillisecond = 3600 * 1000;
        final int oneHourSecond = 3600;
        Date startTime = (Date) redisTemplate.opsForValue().get("-"+userCode + "startTest" + testId);
        String port = (String) redisTemplate.opsForValue().get("-"+userCode + "port" + testId);
        Object seeAnswer = redisTemplate.opsForValue().get("-" + userCode + "seeAnswer" + testId);
        Map<String, Object> map = new HashMap<>();

        if (port != null) {
            map.put("port",port);
        }else {
            map.put("port","等待测试中");
        }
        if (seeAnswer != null) {
            map.put("seeAnswer",seeAnswer);
        }

        if (startTime == null) {
            map.put("lastTime",oneHourSecond);
            return Result.successResult(map);
        }


        if ((System.currentTimeMillis() - startTime.getTime()) > oneHourMillisecond) {
            redisTemplate.delete("-"+userCode + "startTest" + testId);
            map.put("lastTime",oneHourSecond);
            return Result.successResult(map);
        }
        long lastTime = (oneHourMillisecond - (System.currentTimeMillis() - startTime.getTime())) / 1000;

        map.put("lastTime", lastTime);
        return Result.successResult(map);
    }

    /**
     * 启动靶场,
     *
     * @param request 包含用户信息的请求
     * @param testId  要开始的靶场ID
     * @return 返回操作结果，成功时返回靶场访问地址，失败时返回错误信息
     */
    @RequestMapping("startTest")
    public Result<String> startTest(HttpServletRequest request, Long testId, String oldFlag) throws Exception {
        Long userCode = Long.valueOf(UserTokenUtil.getUserAccount(request));

        ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
        Set<String> keys = redisTemplate.keys("-" + userCode + "startTest*");
        if (keys != null && keys.size() > 0) {
            return Result.failResult("已经开始了一个靶场");
        }

        String flag = Md5Util.encode(oldFlag + userCode + System.currentTimeMillis());

        opsForValue.set("-"+userCode + "-flag-" + testId, flag, 1, TimeUnit.HOURS);

        opsForValue.set("-"+userCode + "-flag-" + testId + "-time", new Date(), 1, TimeUnit.HOURS);

        Map<String, Object> infoMap = new HashMap<>();
        infoMap.put("userCode", userCode);
        infoMap.put("testId", testId);
        infoMap.put("redisKey", userCode + "startTest" + testId);
        rabbitTemplate.convertAndSend("startTestExchange", "startTest", infoMap);
        Map<String, String> resultMap = testDetailService.startTest(testId, userCode);
        Map<String, Object> flagMap = new HashMap<>();
        flagMap.put("flag", flag);
        flagMap.put("url", resultMap.get("ip"));
        log.info("生成的ip是:"+resultMap.get("ip")+"端口号为"+resultMap.get("port"));
        rabbitTemplate.convertAndSend("sendFlagExchange", "sendFlag", flagMap);

        opsForValue.set("-"+userCode + "startTest" + testId, new Date(), 1, TimeUnit.HOURS);

        String result = "http://192.168.111.130:" + resultMap.get("port");
        opsForValue.set("-"+userCode + "port" + testId, result, 2, TimeUnit.HOURS);
        return Result.successResult(result);
    }
    /**
     * 根据靶场Code删除一条靶场记录
     *
     * @param testCode 靶场编号
     * @return 操作结果，成功返回ok，失败返回删除失败！
     * @throws Exception 如果删除过程中发生异常，则抛出异常
     */
    @RequestMapping("deleteTestDetail")
    public Result deleteTestDetail(Long testCode) throws Exception {
        Boolean result = testDetailService.deleteTestDetail(testCode);
        if (result) {
            return Result.successResult("ok");
        } else {
            return Result.failResult("删除失败！");
        }
    }
    /**
     * 更新一条靶场记录
     *
     * @param testVo 待更新的靶场记录对象
     * @return 操作结果，成功返回ok，失败返回修改失败！
     */
    @RequestMapping("updateTestDetail")
    public Result updateTestDetail(TestVo testVo) {
        Boolean result = testDetailService.updateTestDetail(testVo);
        if (result) {
            return Result.successResult("ok");
        } else {
            return Result.failResult("修改失败！");
        }
    }
    /**
     * 提交Flag，验证用户输入的flag是否正确，并根据验证结果计算用户的得分，并关闭靶场环境
     * @param request HTTP请求对象
     * @param testId 靶场环境的ID
     * @param flag 用户输入的Flag
     * @return 返回包含验证结果的Result对象，若Flag正确则返回验证成功和用户得分，否则返回失败信息
     * @throws Exception 可能会抛出异常
     */
    @PostMapping("submitFlag")
    public Result<String> submitFlag(HttpServletRequest request, Long testId, String flag) throws Exception {
        Long userCode = Long.valueOf(UserTokenUtil.getUserAccount(request));

        String redisFlag = (String) redisTemplate.opsForValue().get("-"+userCode + "-flag-" + testId);
        if (redisFlag == null) {
            return Result.failResult("没有开始测试");
        }
        if (!redisFlag.equals(flag)) {
            return Result.failResult("flag错误");
        }
        testDetailService.calculateByScore(userCode, testId);

        testDetailService.closeTest(testId, userCode);

        return Result.successResult("验证成功,恭喜你通过了测试");
    }
    /**
     * 延长靶场时间，将Redis中靶场环境的过期时间延长至1小时
     * @param request HTTP请求对象
     * @param testId 靶场环境的ID
     * @return 返回包含延期结果的Result对象，若延期成功则返回成功信息
     */
    @GetMapping("delayTest")
    public Result<String> delayTest(HttpServletRequest request, Long testId) {
        String userCode = UserTokenUtil.getUserAccount(request);
        redisTemplate.expire("-"+userCode + "-flag-" + testId, 1, TimeUnit.HOURS);
        redisTemplate.expire("-"+userCode + "-flag-" + testId + "-time", 1, TimeUnit.HOURS);
        redisTemplate.opsForValue().set("-"+userCode + "startTest" + testId,new Date(), 1, TimeUnit.HOURS);
        redisTemplate.opsForValue().set("-"+userCode + "-testDelay-" + testId, new Date(), 1, TimeUnit.HOURS);
        return Result.successResult("延期成功");
    }
    /**
     * 获取用户是否延时过靶场
     *
     * @param request http请求对象
     * @param testId  靶场code
     * @return Result对象，表示用户是否延时了靶场
     */
    @GetMapping("getIsDelay")
    public Result<Boolean> getIsDelay(HttpServletRequest request, Long testId) {
        String userCode = UserTokenUtil.getUserAccount(request);
        Object o = redisTemplate.opsForValue().get("-"+userCode + "-testDelay-" + testId);
        if (o == null) {
            return Result.successResult(false);
        }
        return Result.successResult(true);
    }
    @GetMapping("seeCurse")
    public Result<String> seeCurse(HttpServletRequest request, Long testId) {
        String userCode = UserTokenUtil.getUserAccount(request);
        redisTemplate.opsForValue().set("-"+userCode + "seeAnswer" + testId, new Date(), 1, TimeUnit.HOURS);

        return Result.successResult("ok");
    }
    @GetMapping("closeTest")
    public Result<String> closeTest(HttpServletRequest request, Long testId) throws Exception {
        String userCode = UserTokenUtil.getUserAccount(request);
        testDetailService.closeTest(testId, Long.valueOf(userCode));
        return Result.successResult("ok");
    }
}

